1 Data Import and Processing

These analyses pull data from production and development DHIS2 environments to assess client movement.

Data look like this:

#read file from production and view

raw4<-suppressMessages(read_csv("BrianDs.csv"))

#View(raw4)
#colnames(raw4)

head(raw4)
## # A tibble: 6 x 8
##   EnrOrgUnit  ENrUserType TeiUid StageUid VisitNo EvtOrgUnit EvtUserType GestAge
##   <chr>       <chr>       <chr>  <chr>      <dbl> <chr>      <chr>       <chr>  
## 1 uyaWnaBmAs1 HA          rbodU~ Ty22Qt2~       1 uyaWnaBmA~ HA          25.3   
## 2 uyaWnaBmAs1 HA          kkpNN~ Ty22Qt2~       1 uyaWnaBmA~ HA          13.4   
## 3 uyaWnaBmAs1 HA          v14CD~ Ty22Qt2~       1 uyaWnaBmA~ HA          19.4   
## 4 uyaWnaBmAs1 HA          vd7bT~ Ty22Qt2~       1 uyaWnaBmA~ HA          25.1   
## 5 uyaWnaBmAs1 HA          trPZE~ Ty22Qt2~       1 uyaWnaBmA~ HA          25.3   
## 6 gtUM8Eraqvu HA          TdT1e~ Ty22Qt2~       1 gtUM8Eraq~ HA          21.4

Data from development environment

#now get background data from dev
#get Org Unit groups
baseurl<-"https://bd-eregistry.dhis2.org/dhis/"
username<-"ing_test"


#function for logging in
loginDHIS2<-function(baseurl,username,password) {
  url<-paste0(baseurl,"api/me")
  r<-GET(url,authenticate(username,password))
  warn_for_status(r, task="log in")
  if(r$status_code == 200L){return(TRUE)}
}


if(loginDHIS2(baseurl, username, password)==TRUE){
  print("successfully logged in")
}else{
  stop("could not log in! Please check url, username and password")
}
## [1] "successfully logged in"
#groups
url<-paste0(baseurl, "api/organisationUnitGroups.json?paging=false&fields=id,name,organisationUnits")
ou_groups<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>% 
  data.frame() %>% 
  select("name"=1,"id"=2,"members"=3) %>% 
  unnest_longer(members) %>% 
  flatten()
head(ou_groups)
##                                 name          id  members.id
## 1 10/20/31/50 Bed Hospital (not UHC) iXQnUg4Ayjg        <NA>
## 2         Baganbari Union, Uttar Mat qzxlprBwoN2 TNlLoHITukJ
## 3         Baganbari Union, Uttar Mat qzxlprBwoN2 qDuy7VCWXEu
## 4         Baganbari Union, Uttar Mat qzxlprBwoN2 Law6euHPccf
## 5         Baganbari Union, Uttar Mat qzxlprBwoN2 kKuwT6hXLqh
## 6         Baganbari Union, Uttar Mat qzxlprBwoN2 oKhlabKlBHa
#stages
url<-paste0(baseurl, "api/programStages.json?paging=false&fields=id,name")
ps<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>% 
  data.frame() %>% 
  select("id"=2, "psname"=1)
head(ps)
##            id              psname
## 1 piRv8jtcLQV      ANC 1st visit_
## 2 WZbXY0S00lP       ANC 1st visit
## 3 h6idWg9SfPr ANC Follow up sheet
## 4 ViHTJrKKrFg      ANC Green File
## 5 tlzRiafqzgd      ANCManagements
## 6 iXDSolqmauJ            ANCRisks
#OU names
url<-paste0(baseurl, "api/organisationUnits.json?paging=false&fields=id,name")
ou_names<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>% 
  data.frame() %>% 
  select("name"=1, "ou_id"=2) %>% 
  mutate("ou_type"=case_when(
    str_detect(name, " FWC") ~ "FWC",
    str_detect(name, "(?i)CC") ~ "CC",
    str_detect(name, "Unit") ~ "Unit",
    str_detect(name, "Ward") ~ "Ward"))

head(ou_names)
##                               name       ou_id ou_type
## 1                    Aburkandi FWC mf3RMT3QRhf     FWC
## 2 Aithadi Pacani CC, Matlab(North) si7FoHyJpZ0      CC
## 3       Amiapur Cc - Matlab(north) P1okfUePFEd      CC
## 4                     Aswinpur FWC ubVXGg2leZa     FWC
## 5    BAGANBARI UNION, UTTAR MATLAB mR8aUxvBDgQ    <NA>
## 6             BAHERCHOR LOTURDI CC PduJMfKZhFt      CC

1.1 Data Processing

We then merge the raw outputs of TEI events with org unit information

If arrange events by visit number, classify subsequent visits as being at same or different org unit than the initial visit.

my_data<-raw4 %>% 
  left_join(ou_names, by=c("EvtOrgUnit"="ou_id")) %>% 
  select(tei=3, ga=8, "ou_id"=EvtOrgUnit, ou_type, name, VisitNo, EnrOrgUnit, StageUid) %>% 
  distinct() %>% #event must be unique visit, i.e. an ANC management and ANC stage same week would be merged
  mutate(ga=round(as.numeric(ga))) %>% 
  filter(!is.na(ou_type) & !is.na(ga) & ga <= 50 & ga >= 1) %>% 
  mutate(VisitNo=if_else(VisitNo > 6, "7+", as.character(VisitNo))) %>% 
  mutate(VisitNo=as.factor(VisitNo)) %>% 
  mutate(ou_type=recode_factor(ou_type, "FWC"="FWC", "CC"="CC", "Ward"="Ward", "Unit"="Unit" )) %>% 
  group_by(tei) %>% 
  add_tally() %>% 
  arrange(tei, ga) %>% 
  mutate(first = dplyr::first(ou_id)) %>%
  mutate(ga_initial = dplyr::first(ga)) %>% 
  mutate(last_ou = lag(ou_id)) %>% 
  mutate(Moved_ou = case_when(first == ou_id & is.na(last_ou)  ~ "Event 1",
                              first == ou_id & !is.na(last_ou) ~ "Event 2+, same ou as Event 1", 
                              first != ou_id ~ "Event 2+, different ou as Event 1")) %>% 
  mutate(Moved_ou_wrap = str_wrap(Moved_ou, width = 20))

#my_data

head(my_data)
## # A tibble: 6 x 14
## # Groups:   tei [4]
##   tei      ga ou_id ou_type name  VisitNo EnrOrgUnit StageUid     n first
##   <chr> <dbl> <chr> <fct>   <chr> <fct>   <chr>      <chr>    <int> <chr>
## 1 A0du~    21 HWOI~ Ward    Ward~ 1       HWOIuGUJV~ Ty22Qt2~     1 HWOI~
## 2 a0lM~    40 P1ok~ CC      Amia~ 1       P1okfUePF~ Ty22Qt2~     2 P1ok~
## 3 a0lM~    40 P1ok~ CC      Amia~ 1       P1okfUePF~ WZbXY0S~     2 P1ok~
## 4 a0Mz~    25 ZiEo~ Unit    Unit~ 1       ZiEozn9m9~ Ty22Qt2~     1 ZiEo~
## 5 a0RW~    18 uMnk~ Ward    Ward~ 1       uMnkQBPF2~ Ty22Qt2~     2 uMnk~
## 6 a0RW~    33 Sr8A~ FWC     Shak~ 1       uMnkQBPF2~ WZbXY0S~     2 uMnk~
## # ... with 4 more variables: ga_initial <dbl>, last_ou <chr>, Moved_ou <chr>,
## #   Moved_ou_wrap <chr>

2 Visualization

Let’s start simple with a histogram. GA of each visit, by OU type.

p<-ggplot(my_data, aes(ga))+
  geom_histogram(bins=25)+
  facet_wrap(~ou_type, ncol=1)+
  labs(title="Events by GA and OU type")
p

2.1 Density Plot by Org Unit type and Gestational Age

Another way to show this histogram is a density dot plot. To make it interesting we can animate it, to emphasize this progression of time. It looks a bit like a paint roller…

p<-ggplot(my_data, aes(ga, ou_type)) +
  geom_jitter(aes(group = ga, size = .3), height = 0.25, show.legend = FALSE) +
  labs(title="Pregnancy events in e-Reg Matlab by Org Unit",
       subtitle = 'Visits at Gestational Age {closest_state}',
       y = 'Org Unit Type') +
 # scale_colour_manual(values = col_scale) +
  transition_states(ga, transition_length = 3, state_length = 2) +
  shadow_mark(size = .5) +
  ease_aes('linear')

animate(
  plot = p, 
  nframes = 200,
  duration = 15,
  end_pause = 50
)

But this doesnt say much about patient movement. When do clients move to a different org unit clinic?

  • The red dots are the GA and location (org unit) of first event (identification).

  • The blue dots are subsequent events that are at same location as first event.

  • The green dots are movement to a DIFFERENT location than the identification org unit.

p<-ggplot(my_data, aes(ga, Moved_ou_wrap)) +
  geom_jitter(aes(group = ga, color = Moved_ou_wrap), size = 0.01) +
  facet_wrap(~ou_type, ncol = 1)+
    theme(legend.position = "none") +
    labs(title="Pregnancy events in e-Reg Matlab by Org Unit") +
    ylab("")

p

Same thing, but animated…

p<-p +
  labs(title="Pregnancy events in e-Reg Matlab by Org Unit",
       subtitle = 'Visits at Gestational Age {closest_state}') +
  theme(legend.position = "none") +
  transition_states(ga, transition_length = 3, state_length = 2) +
  shadow_mark(size = .3) +
  ease_aes('linear')

animate(
  plot = p, 
  nframes = 200,
  duration = 15,
  end_pause = 50
)

Now we can see that most movement to new place happens after 36 weeks for FWA units and HA Wards (Home PPC follow up for those not identified by FWA). But women move to FWC between 14 and 35 weeks. Fewer women choose to move to CC, if they were identified elsewhere.

We want to narrow in on the patients who start at one org unit, and receive services at another. This approach tells us what kind of org unit they GO to, but not what kind of org unit they COME from.

2.2 Chord diagram

Chord diagram might be helpful too here - to do.

2.3 Heat Map

If we narrow in on the patients who move to a new location (the green dots above), we can see the overlap of service provision between types of org unit.

Here is a table of events that are at a different org unit than the enrollment org unit, arranged by org unit type.

pcords_data <-my_data %>% 
  ungroup() %>% 
  filter(Moved_ou=="Event 2+, different ou as Event 1") %>% 
  rename("event_ou_id"=ou_id, "event_ou_type"=ou_type) %>% 
  left_join(ou_names, by=c("EnrOrgUnit"="ou_id")) %>% 
  rename("enr_ou_type"=ou_type) %>%
  select(enr_ou_type, event_ou_type, "event_ga"=ga, "enr_ga"=ga_initial) 


test<-pcords_data %>%
  arrange(event_ou_type) %>% 
  mutate(event_ou_type=factor(event_ou_type, levels=c("CC","FWC","Unit","Ward"))) %>% 
  group_by(enr_ou_type, event_ou_type) %>% 
  select("enrollment OU"=enr_ou_type, "event OU"=event_ou_type) %>% 
  summarize(count=n())

kable(test) %>%
  kable_styling()
enrollment OU event OU count
CC CC 4
CC FWC 29
CC Unit 9
FWC CC 11
FWC FWC 8
FWC Unit 10
Unit CC 100
Unit FWC 298
Unit Unit 27
Unit Ward 16
Ward CC 14
Ward FWC 73
Ward Unit 4

We can visualize this graph in a heatmap

# Give extreme colors:
library(viridis)

ggplot(test, aes(`enrollment OU`, `event OU`, fill= count)) + 
  geom_tile() +
  scale_fill_viridis(discrete=FALSE) +
  theme_minimal()

We can facet these down by enrollment OU, then show the event GA for each subsequent event.

heat2<-pcords_data %>%
    mutate(event_ou_type=factor(event_ou_type, levels=c("CC","FWC","Unit","Ward"))) %>% 
  mutate(gestage_event=case_when(
    event_ga >= 0 & event_ga < 18 ~ "0-17",
    event_ga >= 18 & event_ga < 24 ~ "18-23",
    event_ga >= 24 & event_ga < 29 ~ "24-29",
    event_ga >= 29 & event_ga < 34 ~ "29-33",
    event_ga >= 34 & event_ga < 40 ~ "34-39",
                     event_ga >= 40 ~ "40+",
  )) %>% 
  group_by(enr_ou_type, event_ou_type, gestage_event) %>% 
  select("enrollment OU"=enr_ou_type, "event OU"=event_ou_type, gestage_event) %>% 
  summarize(count=n())


ggplot(heat2, aes(gestage_event, `event OU`, fill= count)) + 
  geom_tile() +
  scale_fill_viridis(discrete=FALSE) +
  facet_wrap(~`enrollment OU`,labeller = "label_both")+
  labs(title="Events at different OU than enrollment",
       subtitle="By Event Gest Age and OU Type")

2.4 Animated Dot Plot

What are the patterns over time though?

In this animation, we focus on the “green dots” above. Each green dot is an event. The horizontal lines represent a type of org unit. The middle grey dots represent the identification events–once they cross that dot, the pregnancy is identified.

After that, clients go to many other types of org units. Some go to a different org unit of same type, while others go to a new org unit type.

By animating this over gestational age at visit, we can see which weeks had highest “crossover” of events. The speed of dot movement represents the time between visits.

pcord2<-pcords_data %>% 
rownames_to_column(var="id") %>% 
mutate("enr_start"=enr_ou_type) %>% 
pivot_longer(c('enr_start', 'enr_ou_type','event_ou_type'), 
             names_to = "start_finish", 
             values_to="ou_type") %>% 
  mutate(gestage=if_else(start_finish=="enr_ou_type", enr_ga, 
                          if_else(start_finish=="event_ou_type", event_ga, 0))) %>% 
  mutate(endpoint=if_else(start_finish=="enr_start", 0,
                          if_else(start_finish=="enr_ou_type", 1, 2))) %>% 
  mutate(ou_type=factor(ou_type, levels = c("Unit","Ward","FWC","CC"))) %>% 
  mutate(gestage=if_else(endpoint==2 & enr_ga > 30 & gestage > 30, gestage + 2, gestage)) %>% 
  mutate(gestage=if_else(endpoint==2, gestage + 1, gestage))


pcord_summ<-pcord2 %>% 
  filter(endpoint!=0) %>% 
  group_by(endpoint, ou_type, gestage) %>% 
  summarise("count"=n_distinct(id)) %>% 
  mutate("cumsum"=cumsum(count))

#pcord2 %>% filter(endpoint==2 & gestage > 35)


ps1<-pcord_summ %>% filter(endpoint==1)
ps2<-pcord_summ %>% filter(endpoint==2)

p2<-ggplot() +
  geom_point(data = pcord2, aes(x=endpoint, y = ou_type, 
                                group = id), col = "green")  +
  geom_point(data = ps1, aes(x=endpoint, y = ou_type, size = cumsum),
                                col="grey", alpha=0.8) +
  geom_point(data = ps2, aes(x=endpoint, y = ou_type, size = cumsum),
                               col="grey", alpha=0.8) +
  theme_minimal() + 
  transition_reveal(gestage) +
  scale_x_continuous(breaks=c(0, 1, 2),
                   labels=c("GA 0","Identification", "Other Event")) +
  labs(title="e-Reg Matlab -- Events at Different Org Unit than Identification",
       subtitle = 'Events at Gestational Age {round(frame_along)}',
       x = "")

animate(p2,
        nframes = 200,
        duration = 15,
        end_pause = 50)

If a dot moves quickly between identification and subsequent visit, that means that the next visit happened quickly after identification (e.g., Unit identifies pregnancy at 16 weeks, visit to CHCP at 18 weeks). Inversely, the second visit may occur long after identification (same client visits CHCP again at 32 weeks, this would be a slower moving dot).

Eventually want to recreate as a Sankey flow diagram across 4 ANC visits. See below.

2.5 Parallel Coordinates

If we want to explore these relationships further, we can use interactive visualization.

The type below is called parallel coordinates.

The vertical axes represent variables. The horizontal and diagonal lines are observations, where each is an EVENT that took at a different place than the enrollment org unit. The colors are based on the type of enrollment org unit.

You can click and drag across an axis to select a range for each variable, and it will filter down to the observations that meet that criteria. For example, of those clients who were enrolled at a WARD, and later went to a CC, what was the range of gestational ages when that CC visit took place?

##### Parallel Coordinates Graph ######
library(parcoords)
#parallel coordinates with color based on gender

parcoords::parcoords(data = pcords_data,
                     rownames = TRUE,
                     color = list(
                       # discrete or categorical column
                       colorScale = "scaleOrdinal",
                       colorBy = "enr_ou_type",
                       colorScheme = "schemeCategory10"),
                     withD3 = TRUE,
                     brushMode = "1D-axes-multi",
                     alphaOnBrushed = 0.2,
                     queue = TRUE,
                     rate = 50,
                     reorderable = TRUE)

To make the correlations easier to read, you can drag and rearrange the axes order.

We might expand on this by linking to the selected ranges to tables, for dynamic filtering of data.

3 Dropouts: Isolating Clients with only one event

If the client has only one event in system, maybe they are different for some reason than the other events.

For example, what stage was their only event?

What kind of org unit?

The below tables are only one event.

First is by org unit – most of these are the Pregnancy ID stage.

library(kableExtra)
###Isolate those that only havd one event
my_data_iso<-raw4 %>% 
  left_join(ou_names, by=c("EvtOrgUnit"="ou_id")) %>% 
  select(tei=3, ga=8, "ou_id"=EvtOrgUnit, ou_type, name, VisitNo, EnrOrgUnit, StageUid) %>%
  left_join(ps, by = c("StageUid"="id")) %>% 
  mutate(ga=round(as.numeric(ga))) %>% 
  filter(!is.na(ou_type) & !is.na(ga) & 
           ga <= 50 & ga >= 1 &
          str_detect(psname, paste(c("regnanc", "ANC", "Newborn","PNC","Lab"),collapse = '|')) &
          !str_detect(psname, paste(c("Prev","Risk","Manag"),collapse = '|'))) %>% 
  group_by(tei) %>% 
  add_tally() %>% 
  arrange(tei, ga) %>% 
  mutate(first = dplyr::first(ou_id)) %>%
  mutate(last_ou = lag(ou_id)) %>%
  ungroup() %>% 
  mutate(Moved_ou = case_when(first == ou_id & is.na(last_ou)  ~ "Event 1",
                              first == ou_id & !is.na(last_ou) ~ "Event 2+, same ou as Event 1", 
                              first != ou_id ~ "Event 2+, different ou as Event 1"))

# my_data_iso %>% 
#   group_by(ou_type, n) %>% 
#   summarise("events"= n()) %>% 
#   mutate(percent = round(events/sum(events), 2))

#my_data_iso

test2<-my_data_iso %>% 
  group_by(tei) %>% 
  filter(n==1) %>% 
  group_by(psname) %>% 
  summarize("stage_count"=n())

test3<-my_data_iso %>% 
  filter(n==1) %>% 
  group_by(ou_type) %>% 
  summarize("ou_type_count"=n())

#kableExtra::kable(test2)
kable(test2) %>% 
  kable_styling()
psname stage_count
ANC 1st visit_ 2
গর্ভকালীন সেবার তথ্য (Home - ANC record) 4
গর্ভাবস্থার সনাক্তকরণ (Pregnancy identification) 2775
ANC 1st visit 1
ANC visit 6
ANC visit_ 2

Next is by org unit. Most of these are the FWA Units.

kable(test3) %>% 
  kable_styling()
ou_type ou_type_count
CC 72
FWC 46
Unit 1887
Ward 785

4 Cascade by Visit Number

This section shows ALL VISITS for each patient We can visualize the visit number (x axis), GA at visit (time), and type of org unit at each visit (y axis). The inspiration is this NYTimes infographic

Each dot represents a patient as she moves through each level of the health system. The patient’s dot “rests” at the location of the last visit recorded. Note that the this considers each identification, ANC visit, or home visit stage a separate “visit”.

You can see that very few patients get past the 3rd visit at any level. Most of the migration occurs from the FWA Unit level up the system to FWC. And compared to CC level, more clients who made it to FWC by the third visit started from another org unit (red dots).

cascade_data <-my_data %>% 
  ungroup() %>% 
  rename("event_ou_id"=ou_id, "event_ou_type"=ou_type) %>% 
  select(tei, event_ou_type, "event_ga"=ga, Moved_ou, StageUid) %>% 
  left_join(ps, by = c("StageUid"="id")) %>% 
  filter(!is.na(event_ou_type) & !is.na(event_ga) & 
           event_ga <= 50 & event_ga >= 1 &
          str_detect(psname, paste(c("regnanc", "ANC"),collapse = '|')) &
          !str_detect(psname, paste(c("Prev","Risk","Manag", "Out"),collapse = '|'))) %>% 
  group_by(tei) %>% 
  mutate("VisitNo"=if_else(str_detect(psname, "ident"), 1, 2)) %>% 
  arrange(VisitNo, event_ga) %>% 
  mutate("VisitNo"=row_number()) 

cascade_data<-cascade_data %>% 
  mutate("VisitNo"=if_else(VisitNo==1, 0, as.double(VisitNo))) %>% 
  bind_rows(cascade_data %>%  filter(VisitNo == 1)) %>% 
  mutate("Moved_ou"=if_else(VisitNo <=1, "Event 1", Moved_ou)) %>% 
  mutate(event_ou_type=factor(event_ou_type, levels = c("Unit","Ward","FWC","CC"))) %>% 
  mutate(groupid= group_indices()) %>% 
  arrange(tei, VisitNo) %>% 
  mutate(gestage=if_else(VisitNo==0, 0, event_ga)) %>% 
  filter(VisitNo<8) %>% 
  mutate(gestage=if_else(gestage==lag(gestage) & groupid==lag(groupid) & gestage!=0, 
                         gestage+2, gestage))



p3<-ggplot() +
  geom_jitter(data = cascade_data, aes(x=VisitNo, y = event_ou_type, 
                                group = groupid, col = Moved_ou), 
                                size=0.5, width = 0.15, height=0.1)  +
  theme_minimal() + 
  transition_reveal(gestage) +
  scale_x_continuous(breaks=c(0:7),
                   labels=c("GA 0", "Event1", "Event2",
                            "Event3","Event4","Event5", "Event6","Event7"),
                   minor_breaks = NULL) +
  scale_colour_manual(values = c("grey", "red", "darkblue")) +
  labs(title="e-Reg Matlab -- Events 1-7",
       subtitle = 'Events at Gestational Age {round(frame_along)}',
       x = "",
       y="")+
  theme(legend.position = "bottom")


animate(p3,
        nframes = 200,
        duration = 15,
        end_pause = 50)

#gganimate::anim_save("bd_visit1to7.gif")
LS0tDQp0aXRsZTogIkNsaWVudCBNb3ZlbWVudCBpbiBlLVJlZyBNYXRsYWI6IFZpc3VhbCBBbmFseXNpcyINCmF1dGhvcjogIkJyaWFuIE8nRG9ubmVsbCINCmRhdGU6ICI1LzgvMjAyMCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpDQoNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdnYW5pbWF0ZSkNCmxpYnJhcnkoaHR0cikNCmxpYnJhcnkoanNvbmxpdGUpDQpsaWJyYXJ5KGFzc2VydHRoYXQpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHZpcmlkaXMpDQpwYXNzd29yZDwtcmVhZF9saW5lcygicGFzc3cudHh0IikNCg0KYGBgDQoNCiMgRGF0YSBJbXBvcnQgYW5kIFByb2Nlc3NpbmcNCg0KVGhlc2UgYW5hbHlzZXMgcHVsbCBkYXRhIGZyb20gcHJvZHVjdGlvbiBhbmQgZGV2ZWxvcG1lbnQgREhJUzIgZW52aXJvbm1lbnRzIHRvIGFzc2VzcyBjbGllbnQgbW92ZW1lbnQuDQoNCkRhdGEgbG9vayBsaWtlIHRoaXM6DQoNCmBgYHtyIHByb2QgZGF0YX0NCg0KI3JlYWQgZmlsZSBmcm9tIHByb2R1Y3Rpb24gYW5kIHZpZXcNCg0KcmF3NDwtc3VwcHJlc3NNZXNzYWdlcyhyZWFkX2NzdigiQnJpYW5Ecy5jc3YiKSkNCg0KI1ZpZXcocmF3NCkNCiNjb2xuYW1lcyhyYXc0KQ0KDQpoZWFkKHJhdzQpDQoNCmBgYA0KDQoNCkRhdGEgZnJvbSBkZXZlbG9wbWVudCBlbnZpcm9ubWVudA0KDQpgYGB7ciBkZXYgZGF0YX0NCiNub3cgZ2V0IGJhY2tncm91bmQgZGF0YSBmcm9tIGRldg0KI2dldCBPcmcgVW5pdCBncm91cHMNCmJhc2V1cmw8LSJodHRwczovL2JkLWVyZWdpc3RyeS5kaGlzMi5vcmcvZGhpcy8iDQp1c2VybmFtZTwtImluZ190ZXN0Ig0KDQoNCiNmdW5jdGlvbiBmb3IgbG9nZ2luZyBpbg0KbG9naW5ESElTMjwtZnVuY3Rpb24oYmFzZXVybCx1c2VybmFtZSxwYXNzd29yZCkgew0KICB1cmw8LXBhc3RlMChiYXNldXJsLCJhcGkvbWUiKQ0KICByPC1HRVQodXJsLGF1dGhlbnRpY2F0ZSh1c2VybmFtZSxwYXNzd29yZCkpDQogIHdhcm5fZm9yX3N0YXR1cyhyLCB0YXNrPSJsb2cgaW4iKQ0KICBpZihyJHN0YXR1c19jb2RlID09IDIwMEwpe3JldHVybihUUlVFKX0NCn0NCg0KDQppZihsb2dpbkRISVMyKGJhc2V1cmwsIHVzZXJuYW1lLCBwYXNzd29yZCk9PVRSVUUpew0KICBwcmludCgic3VjY2Vzc2Z1bGx5IGxvZ2dlZCBpbiIpDQp9ZWxzZXsNCiAgc3RvcCgiY291bGQgbm90IGxvZyBpbiEgUGxlYXNlIGNoZWNrIHVybCwgdXNlcm5hbWUgYW5kIHBhc3N3b3JkIikNCn0NCg0KI2dyb3Vwcw0KdXJsPC1wYXN0ZTAoYmFzZXVybCwgImFwaS9vcmdhbmlzYXRpb25Vbml0R3JvdXBzLmpzb24/cGFnaW5nPWZhbHNlJmZpZWxkcz1pZCxuYW1lLG9yZ2FuaXNhdGlvblVuaXRzIikNCm91X2dyb3VwczwtZnJvbUpTT04oY29udGVudChHRVQodXJsKSwgInRleHQiKSwgZmxhdHRlbiA9IFRSVUUpICU+JSANCiAgZGF0YS5mcmFtZSgpICU+JSANCiAgc2VsZWN0KCJuYW1lIj0xLCJpZCI9MiwibWVtYmVycyI9MykgJT4lIA0KICB1bm5lc3RfbG9uZ2VyKG1lbWJlcnMpICU+JSANCiAgZmxhdHRlbigpDQpoZWFkKG91X2dyb3VwcykNCiAgDQojc3RhZ2VzDQp1cmw8LXBhc3RlMChiYXNldXJsLCAiYXBpL3Byb2dyYW1TdGFnZXMuanNvbj9wYWdpbmc9ZmFsc2UmZmllbGRzPWlkLG5hbWUiKQ0KcHM8LWZyb21KU09OKGNvbnRlbnQoR0VUKHVybCksICJ0ZXh0IiksIGZsYXR0ZW4gPSBUUlVFKSAlPiUgDQogIGRhdGEuZnJhbWUoKSAlPiUgDQogIHNlbGVjdCgiaWQiPTIsICJwc25hbWUiPTEpDQpoZWFkKHBzKQ0KDQojT1UgbmFtZXMNCnVybDwtcGFzdGUwKGJhc2V1cmwsICJhcGkvb3JnYW5pc2F0aW9uVW5pdHMuanNvbj9wYWdpbmc9ZmFsc2UmZmllbGRzPWlkLG5hbWUiKQ0Kb3VfbmFtZXM8LWZyb21KU09OKGNvbnRlbnQoR0VUKHVybCksICJ0ZXh0IiksIGZsYXR0ZW4gPSBUUlVFKSAlPiUgDQogIGRhdGEuZnJhbWUoKSAlPiUgDQogIHNlbGVjdCgibmFtZSI9MSwgIm91X2lkIj0yKSAlPiUgDQogIG11dGF0ZSgib3VfdHlwZSI9Y2FzZV93aGVuKA0KICAgIHN0cl9kZXRlY3QobmFtZSwgIiBGV0MiKSB+ICJGV0MiLA0KICAgIHN0cl9kZXRlY3QobmFtZSwgIig/aSlDQyIpIH4gIkNDIiwNCiAgICBzdHJfZGV0ZWN0KG5hbWUsICJVbml0IikgfiAiVW5pdCIsDQogICAgc3RyX2RldGVjdChuYW1lLCAiV2FyZCIpIH4gIldhcmQiKSkNCg0KaGVhZChvdV9uYW1lcykNCmBgYA0KDQojIyBEYXRhIFByb2Nlc3NpbmcNCg0KV2UgdGhlbiBtZXJnZSB0aGUgcmF3IG91dHB1dHMgb2YgVEVJIGV2ZW50cyB3aXRoIG9yZyB1bml0IGluZm9ybWF0aW9uDQoNCklmIGFycmFuZ2UgZXZlbnRzIGJ5IHZpc2l0IG51bWJlciwgY2xhc3NpZnkgc3Vic2VxdWVudCB2aXNpdHMgYXMgYmVpbmcgYXQgc2FtZSBvciBkaWZmZXJlbnQgb3JnIHVuaXQgdGhhbiB0aGUgaW5pdGlhbCB2aXNpdC4NCg0KYGBge3IgcHJvY2Vzc2luZ30NCg0KbXlfZGF0YTwtcmF3NCAlPiUgDQogIGxlZnRfam9pbihvdV9uYW1lcywgYnk9YygiRXZ0T3JnVW5pdCI9Im91X2lkIikpICU+JSANCiAgc2VsZWN0KHRlaT0zLCBnYT04LCAib3VfaWQiPUV2dE9yZ1VuaXQsIG91X3R5cGUsIG5hbWUsIFZpc2l0Tm8sIEVuck9yZ1VuaXQsIFN0YWdlVWlkKSAlPiUgDQogIGRpc3RpbmN0KCkgJT4lICNldmVudCBtdXN0IGJlIHVuaXF1ZSB2aXNpdCwgaS5lLiBhbiBBTkMgbWFuYWdlbWVudCBhbmQgQU5DIHN0YWdlIHNhbWUgd2VlayB3b3VsZCBiZSBtZXJnZWQNCiAgbXV0YXRlKGdhPXJvdW5kKGFzLm51bWVyaWMoZ2EpKSkgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKG91X3R5cGUpICYgIWlzLm5hKGdhKSAmIGdhIDw9IDUwICYgZ2EgPj0gMSkgJT4lIA0KICBtdXRhdGUoVmlzaXRObz1pZl9lbHNlKFZpc2l0Tm8gPiA2LCAiNysiLCBhcy5jaGFyYWN0ZXIoVmlzaXRObykpKSAlPiUgDQogIG11dGF0ZShWaXNpdE5vPWFzLmZhY3RvcihWaXNpdE5vKSkgJT4lIA0KICBtdXRhdGUob3VfdHlwZT1yZWNvZGVfZmFjdG9yKG91X3R5cGUsICJGV0MiPSJGV0MiLCAiQ0MiPSJDQyIsICJXYXJkIj0iV2FyZCIsICJVbml0Ij0iVW5pdCIgKSkgJT4lIA0KICBncm91cF9ieSh0ZWkpICU+JSANCiAgYWRkX3RhbGx5KCkgJT4lIA0KICBhcnJhbmdlKHRlaSwgZ2EpICU+JSANCiAgbXV0YXRlKGZpcnN0ID0gZHBseXI6OmZpcnN0KG91X2lkKSkgJT4lDQogIG11dGF0ZShnYV9pbml0aWFsID0gZHBseXI6OmZpcnN0KGdhKSkgJT4lIA0KICBtdXRhdGUobGFzdF9vdSA9IGxhZyhvdV9pZCkpICU+JSANCiAgbXV0YXRlKE1vdmVkX291ID0gY2FzZV93aGVuKGZpcnN0ID09IG91X2lkICYgaXMubmEobGFzdF9vdSkgIH4gIkV2ZW50IDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgPT0gb3VfaWQgJiAhaXMubmEobGFzdF9vdSkgfiAiRXZlbnQgMissIHNhbWUgb3UgYXMgRXZlbnQgMSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgIT0gb3VfaWQgfiAiRXZlbnQgMissIGRpZmZlcmVudCBvdSBhcyBFdmVudCAxIikpICU+JSANCiAgbXV0YXRlKE1vdmVkX291X3dyYXAgPSBzdHJfd3JhcChNb3ZlZF9vdSwgd2lkdGggPSAyMCkpDQoNCiNteV9kYXRhDQoNCmhlYWQobXlfZGF0YSkNCg0KYGBgDQoNCiMgVmlzdWFsaXphdGlvbiANCg0KTGV0J3Mgc3RhcnQgc2ltcGxlIHdpdGggYSBoaXN0b2dyYW0uIEdBIG9mIGVhY2ggdmlzaXQsIGJ5IE9VIHR5cGUuDQoNCmBgYHtyIGhpc3QgYmFzaWN9DQpwPC1nZ3Bsb3QobXlfZGF0YSwgYWVzKGdhKSkrDQogIGdlb21faGlzdG9ncmFtKGJpbnM9MjUpKw0KICBmYWNldF93cmFwKH5vdV90eXBlLCBuY29sPTEpKw0KICBsYWJzKHRpdGxlPSJFdmVudHMgYnkgR0EgYW5kIE9VIHR5cGUiKQ0KcA0KDQpgYGANCg0KDQojIyBEZW5zaXR5IFBsb3QgYnkgT3JnIFVuaXQgdHlwZSBhbmQgR2VzdGF0aW9uYWwgQWdlDQoNCkFub3RoZXIgd2F5IHRvIHNob3cgdGhpcyBoaXN0b2dyYW0gaXMgYSBkZW5zaXR5IGRvdCBwbG90LiBUbyBtYWtlIGl0IGludGVyZXN0aW5nIHdlIGNhbiBhbmltYXRlIGl0LA0KdG8gZW1waGFzaXplIHRoaXMgcHJvZ3Jlc3Npb24gb2YgdGltZS4gSXQgbG9va3MgYSBiaXQgbGlrZSBhIHBhaW50IHJvbGxlci4uLg0KDQpgYGB7ciBkZW5zaXR5IGFuaW19DQoNCnA8LWdncGxvdChteV9kYXRhLCBhZXMoZ2EsIG91X3R5cGUpKSArDQogIGdlb21faml0dGVyKGFlcyhncm91cCA9IGdhLCBzaXplID0gLjMpLCBoZWlnaHQgPSAwLjI1LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGxhYnModGl0bGU9IlByZWduYW5jeSBldmVudHMgaW4gZS1SZWcgTWF0bGFiIGJ5IE9yZyBVbml0IiwNCiAgICAgICBzdWJ0aXRsZSA9ICdWaXNpdHMgYXQgR2VzdGF0aW9uYWwgQWdlIHtjbG9zZXN0X3N0YXRlfScsDQogICAgICAgeSA9ICdPcmcgVW5pdCBUeXBlJykgKw0KICMgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xfc2NhbGUpICsNCiAgdHJhbnNpdGlvbl9zdGF0ZXMoZ2EsIHRyYW5zaXRpb25fbGVuZ3RoID0gMywgc3RhdGVfbGVuZ3RoID0gMikgKw0KICBzaGFkb3dfbWFyayhzaXplID0gLjUpICsNCiAgZWFzZV9hZXMoJ2xpbmVhcicpDQoNCmFuaW1hdGUoDQogIHBsb3QgPSBwLCANCiAgbmZyYW1lcyA9IDIwMCwNCiAgZHVyYXRpb24gPSAxNSwNCiAgZW5kX3BhdXNlID0gNTANCikNCg0KDQoNCmBgYA0KDQpCdXQgdGhpcyBkb2VzbnQgc2F5IG11Y2ggYWJvdXQgcGF0aWVudCBtb3ZlbWVudC4gV2hlbiBkbyBjbGllbnRzIG1vdmUgdG8gYSBkaWZmZXJlbnQgb3JnIHVuaXQgY2xpbmljPw0KDQoqIFRoZSByZWQgZG90cyBhcmUgdGhlIEdBIGFuZCBsb2NhdGlvbiAob3JnIHVuaXQpIG9mIGZpcnN0IGV2ZW50IChpZGVudGlmaWNhdGlvbikuIA0KDQoqIFRoZSBibHVlIGRvdHMgYXJlIHN1YnNlcXVlbnQgZXZlbnRzIHRoYXQgYXJlIGF0IHNhbWUgbG9jYXRpb24gYXMgZmlyc3QgZXZlbnQuIA0KDQoqIFRoZSBncmVlbiBkb3RzIGFyZSBtb3ZlbWVudCB0byBhIERJRkZFUkVOVCBsb2NhdGlvbiB0aGFuIHRoZSBpZGVudGlmaWNhdGlvbiBvcmcgdW5pdC4NCg0KYGBge3IgZGVuc2l0eSB3aXRoIGNvbG9yc30NCnA8LWdncGxvdChteV9kYXRhLCBhZXMoZ2EsIE1vdmVkX291X3dyYXApKSArDQogIGdlb21faml0dGVyKGFlcyhncm91cCA9IGdhLCBjb2xvciA9IE1vdmVkX291X3dyYXApLCBzaXplID0gMC4wMSkgKw0KICBmYWNldF93cmFwKH5vdV90eXBlLCBuY29sID0gMSkrDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgbGFicyh0aXRsZT0iUHJlZ25hbmN5IGV2ZW50cyBpbiBlLVJlZyBNYXRsYWIgYnkgT3JnIFVuaXQiKSArDQogICAgeWxhYigiIikNCg0KcA0KDQpgYGANCg0KDQpTYW1lIHRoaW5nLCBidXQgYW5pbWF0ZWQuLi4NCg0KYGBge3IgYW5pbWF0ZWQgcGFpbnQgcm9sbGVyfQ0KcDwtcCArDQogIGxhYnModGl0bGU9IlByZWduYW5jeSBldmVudHMgaW4gZS1SZWcgTWF0bGFiIGJ5IE9yZyBVbml0IiwNCiAgICAgICBzdWJ0aXRsZSA9ICdWaXNpdHMgYXQgR2VzdGF0aW9uYWwgQWdlIHtjbG9zZXN0X3N0YXRlfScpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHRyYW5zaXRpb25fc3RhdGVzKGdhLCB0cmFuc2l0aW9uX2xlbmd0aCA9IDMsIHN0YXRlX2xlbmd0aCA9IDIpICsNCiAgc2hhZG93X21hcmsoc2l6ZSA9IC4zKSArDQogIGVhc2VfYWVzKCdsaW5lYXInKQ0KDQphbmltYXRlKA0KICBwbG90ID0gcCwgDQogIG5mcmFtZXMgPSAyMDAsDQogIGR1cmF0aW9uID0gMTUsDQogIGVuZF9wYXVzZSA9IDUwDQopDQoNCg0KYGBgDQoNCg0KDQpOb3cgd2UgY2FuIHNlZSB0aGF0IG1vc3QgbW92ZW1lbnQgdG8gbmV3IHBsYWNlIGhhcHBlbnMgYWZ0ZXIgMzYgd2Vla3MgZm9yIEZXQSB1bml0cyBhbmQgSEEgV2FyZHMgKEhvbWUgUFBDIGZvbGxvdyB1cCBmb3IgdGhvc2Ugbm90IGlkZW50aWZpZWQgYnkgRldBKS4gQnV0IHdvbWVuIG1vdmUgdG8gRldDIGJldHdlZW4gMTQgYW5kIDM1IHdlZWtzLiBGZXdlciB3b21lbiBjaG9vc2UgdG8gbW92ZSB0byBDQywgaWYgdGhleSB3ZXJlIGlkZW50aWZpZWQgZWxzZXdoZXJlLg0KDQoNCldlIHdhbnQgdG8gbmFycm93IGluIG9uIHRoZSBwYXRpZW50cyB3aG8gc3RhcnQgYXQgb25lIG9yZyB1bml0LCBhbmQgcmVjZWl2ZSBzZXJ2aWNlcyBhdCBhbm90aGVyLiBUaGlzIGFwcHJvYWNoIHRlbGxzIHVzIHdoYXQga2luZCBvZiBvcmcgdW5pdCB0aGV5IEdPIHRvLCBidXQgbm90IHdoYXQga2luZCBvZiBvcmcgdW5pdCB0aGV5IENPTUUgZnJvbS4NCg0KIyMgQ2hvcmQgZGlhZ3JhbQ0KW0Nob3JkIGRpYWdyYW1dKCBodHRwczovL2Jvc3Qub2Nrcy5vcmcvbWlrZS91YmVyZGF0YS8pIG1pZ2h0IGJlIGhlbHBmdWwgdG9vIGhlcmUgLSB0byBkby4NCg0KDQoNCiMjIEhlYXQgTWFwDQoNCklmIHdlIG5hcnJvdyBpbiBvbiB0aGUgcGF0aWVudHMgd2hvIG1vdmUgdG8gYSBuZXcgbG9jYXRpb24gKHRoZSBncmVlbiBkb3RzIGFib3ZlKSwgd2UgY2FuIHNlZSB0aGUgb3ZlcmxhcCBvZiBzZXJ2aWNlIHByb3Zpc2lvbiBiZXR3ZWVuIHR5cGVzIG9mIG9yZyB1bml0Lg0KDQpIZXJlIGlzIGEgdGFibGUgb2YgZXZlbnRzIHRoYXQgYXJlIGF0IGEgKmRpZmZlcmVudCBvcmcgdW5pdCogdGhhbiB0aGUgZW5yb2xsbWVudCBvcmcgdW5pdCwgYXJyYW5nZWQgYnkgb3JnIHVuaXQgdHlwZS4NCg0KYGBge3IgcGNvcmRzIGRhdGF9DQpwY29yZHNfZGF0YSA8LW15X2RhdGEgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBmaWx0ZXIoTW92ZWRfb3U9PSJFdmVudCAyKywgZGlmZmVyZW50IG91IGFzIEV2ZW50IDEiKSAlPiUgDQogIHJlbmFtZSgiZXZlbnRfb3VfaWQiPW91X2lkLCAiZXZlbnRfb3VfdHlwZSI9b3VfdHlwZSkgJT4lIA0KICBsZWZ0X2pvaW4ob3VfbmFtZXMsIGJ5PWMoIkVuck9yZ1VuaXQiPSJvdV9pZCIpKSAlPiUgDQogIHJlbmFtZSgiZW5yX291X3R5cGUiPW91X3R5cGUpICU+JQ0KICBzZWxlY3QoZW5yX291X3R5cGUsIGV2ZW50X291X3R5cGUsICJldmVudF9nYSI9Z2EsICJlbnJfZ2EiPWdhX2luaXRpYWwpIA0KDQoNCnRlc3Q8LXBjb3Jkc19kYXRhICU+JQ0KICBhcnJhbmdlKGV2ZW50X291X3R5cGUpICU+JSANCiAgbXV0YXRlKGV2ZW50X291X3R5cGU9ZmFjdG9yKGV2ZW50X291X3R5cGUsIGxldmVscz1jKCJDQyIsIkZXQyIsIlVuaXQiLCJXYXJkIikpKSAlPiUgDQogIGdyb3VwX2J5KGVucl9vdV90eXBlLCBldmVudF9vdV90eXBlKSAlPiUgDQogIHNlbGVjdCgiZW5yb2xsbWVudCBPVSI9ZW5yX291X3R5cGUsICJldmVudCBPVSI9ZXZlbnRfb3VfdHlwZSkgJT4lIA0KICBzdW1tYXJpemUoY291bnQ9bigpKQ0KDQprYWJsZSh0ZXN0KSAlPiUNCiAga2FibGVfc3R5bGluZygpDQoNCg0KYGBgDQoNCg0KV2UgY2FuIHZpc3VhbGl6ZSB0aGlzIGdyYXBoIGluIGEgaGVhdG1hcA0KDQpgYGB7ciBoZWF0bWFwIDF9DQojIEdpdmUgZXh0cmVtZSBjb2xvcnM6DQpsaWJyYXJ5KHZpcmlkaXMpDQoNCmdncGxvdCh0ZXN0LCBhZXMoYGVucm9sbG1lbnQgT1VgLCBgZXZlbnQgT1VgLCBmaWxsPSBjb3VudCkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPUZBTFNFKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KV2UgY2FuIGZhY2V0IHRoZXNlIGRvd24gYnkgZW5yb2xsbWVudCBPVSwgdGhlbiBzaG93IHRoZSBldmVudCBHQSBmb3IgZWFjaCBzdWJzZXF1ZW50IGV2ZW50Lg0KDQpgYGB7ciBoZWF0bWFwIDJ9DQoNCmhlYXQyPC1wY29yZHNfZGF0YSAlPiUNCiAgICBtdXRhdGUoZXZlbnRfb3VfdHlwZT1mYWN0b3IoZXZlbnRfb3VfdHlwZSwgbGV2ZWxzPWMoIkNDIiwiRldDIiwiVW5pdCIsIldhcmQiKSkpICU+JSANCiAgbXV0YXRlKGdlc3RhZ2VfZXZlbnQ9Y2FzZV93aGVuKA0KICAgIGV2ZW50X2dhID49IDAgJiBldmVudF9nYSA8IDE4IH4gIjAtMTciLA0KICAgIGV2ZW50X2dhID49IDE4ICYgZXZlbnRfZ2EgPCAyNCB+ICIxOC0yMyIsDQogICAgZXZlbnRfZ2EgPj0gMjQgJiBldmVudF9nYSA8IDI5IH4gIjI0LTI5IiwNCiAgICBldmVudF9nYSA+PSAyOSAmIGV2ZW50X2dhIDwgMzQgfiAiMjktMzMiLA0KICAgIGV2ZW50X2dhID49IDM0ICYgZXZlbnRfZ2EgPCA0MCB+ICIzNC0zOSIsDQogICAgICAgICAgICAgICAgICAgICBldmVudF9nYSA+PSA0MCB+ICI0MCsiLA0KICApKSAlPiUgDQogIGdyb3VwX2J5KGVucl9vdV90eXBlLCBldmVudF9vdV90eXBlLCBnZXN0YWdlX2V2ZW50KSAlPiUgDQogIHNlbGVjdCgiZW5yb2xsbWVudCBPVSI9ZW5yX291X3R5cGUsICJldmVudCBPVSI9ZXZlbnRfb3VfdHlwZSwgZ2VzdGFnZV9ldmVudCkgJT4lIA0KICBzdW1tYXJpemUoY291bnQ9bigpKQ0KDQoNCmdncGxvdChoZWF0MiwgYWVzKGdlc3RhZ2VfZXZlbnQsIGBldmVudCBPVWAsIGZpbGw9IGNvdW50KSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9RkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+YGVucm9sbG1lbnQgT1VgLGxhYmVsbGVyID0gImxhYmVsX2JvdGgiKSsNCiAgbGFicyh0aXRsZT0iRXZlbnRzIGF0IGRpZmZlcmVudCBPVSB0aGFuIGVucm9sbG1lbnQiLA0KICAgICAgIHN1YnRpdGxlPSJCeSBFdmVudCBHZXN0IEFnZSBhbmQgT1UgVHlwZSIpDQoNCmBgYA0KDQoNCiMjIEFuaW1hdGVkIERvdCBQbG90DQoNCg0KV2hhdCBhcmUgdGhlIHBhdHRlcm5zIG92ZXIgdGltZSB0aG91Z2g/IA0KDQpJbiB0aGlzIGFuaW1hdGlvbiwgd2UgZm9jdXMgb24gdGhlICJncmVlbiBkb3RzIiBhYm92ZS4gRWFjaCBncmVlbiBkb3QgaXMgYW4gZXZlbnQuIFRoZSBob3Jpem9udGFsIGxpbmVzIHJlcHJlc2VudCBhIHR5cGUgb2Ygb3JnIHVuaXQuIFRoZSBtaWRkbGUgZ3JleSBkb3RzIHJlcHJlc2VudCB0aGUgaWRlbnRpZmljYXRpb24gZXZlbnRzLS1vbmNlIHRoZXkgY3Jvc3MgdGhhdCBkb3QsIHRoZSBwcmVnbmFuY3kgaXMgaWRlbnRpZmllZC4NCg0KQWZ0ZXIgdGhhdCwgY2xpZW50cyBnbyB0byBtYW55IG90aGVyIHR5cGVzIG9mIG9yZyB1bml0cy4gU29tZSBnbyB0byBhIGRpZmZlcmVudCBvcmcgdW5pdCBvZiBzYW1lIHR5cGUsIHdoaWxlIG90aGVycyBnbyB0byBhIG5ldyBvcmcgdW5pdCB0eXBlLg0KDQpCeSBhbmltYXRpbmcgdGhpcyBvdmVyIGdlc3RhdGlvbmFsIGFnZSBhdCB2aXNpdCwgd2UgY2FuIHNlZSB3aGljaCB3ZWVrcyBoYWQgaGlnaGVzdCAiY3Jvc3NvdmVyIiBvZiBldmVudHMuIFRoZSBzcGVlZCBvZiBkb3QgbW92ZW1lbnQgcmVwcmVzZW50cyB0aGUgdGltZSBiZXR3ZWVuIHZpc2l0cy4gDQoNCmBgYHtyIGFuaW1hdGlvbn0NCg0KcGNvcmQyPC1wY29yZHNfZGF0YSAlPiUgDQpyb3duYW1lc190b19jb2x1bW4odmFyPSJpZCIpICU+JSANCm11dGF0ZSgiZW5yX3N0YXJ0Ij1lbnJfb3VfdHlwZSkgJT4lIA0KcGl2b3RfbG9uZ2VyKGMoJ2Vucl9zdGFydCcsICdlbnJfb3VfdHlwZScsJ2V2ZW50X291X3R5cGUnKSwgDQogICAgICAgICAgICAgbmFtZXNfdG8gPSAic3RhcnRfZmluaXNoIiwgDQogICAgICAgICAgICAgdmFsdWVzX3RvPSJvdV90eXBlIikgJT4lIA0KICBtdXRhdGUoZ2VzdGFnZT1pZl9lbHNlKHN0YXJ0X2ZpbmlzaD09ImVucl9vdV90eXBlIiwgZW5yX2dhLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShzdGFydF9maW5pc2g9PSJldmVudF9vdV90eXBlIiwgZXZlbnRfZ2EsIDApKSkgJT4lIA0KICBtdXRhdGUoZW5kcG9pbnQ9aWZfZWxzZShzdGFydF9maW5pc2g9PSJlbnJfc3RhcnQiLCAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKHN0YXJ0X2ZpbmlzaD09ImVucl9vdV90eXBlIiwgMSwgMikpKSAlPiUgDQogIG11dGF0ZShvdV90eXBlPWZhY3RvcihvdV90eXBlLCBsZXZlbHMgPSBjKCJVbml0IiwiV2FyZCIsIkZXQyIsIkNDIikpKSAlPiUgDQogIG11dGF0ZShnZXN0YWdlPWlmX2Vsc2UoZW5kcG9pbnQ9PTIgJiBlbnJfZ2EgPiAzMCAmIGdlc3RhZ2UgPiAzMCwgZ2VzdGFnZSArIDIsIGdlc3RhZ2UpKSAlPiUgDQogIG11dGF0ZShnZXN0YWdlPWlmX2Vsc2UoZW5kcG9pbnQ9PTIsIGdlc3RhZ2UgKyAxLCBnZXN0YWdlKSkNCg0KDQpwY29yZF9zdW1tPC1wY29yZDIgJT4lIA0KICBmaWx0ZXIoZW5kcG9pbnQhPTApICU+JSANCiAgZ3JvdXBfYnkoZW5kcG9pbnQsIG91X3R5cGUsIGdlc3RhZ2UpICU+JSANCiAgc3VtbWFyaXNlKCJjb3VudCI9bl9kaXN0aW5jdChpZCkpICU+JSANCiAgbXV0YXRlKCJjdW1zdW0iPWN1bXN1bShjb3VudCkpDQoNCiNwY29yZDIgJT4lIGZpbHRlcihlbmRwb2ludD09MiAmIGdlc3RhZ2UgPiAzNSkNCg0KDQpwczE8LXBjb3JkX3N1bW0gJT4lIGZpbHRlcihlbmRwb2ludD09MSkNCnBzMjwtcGNvcmRfc3VtbSAlPiUgZmlsdGVyKGVuZHBvaW50PT0yKQ0KDQpwMjwtZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBwY29yZDIsIGFlcyh4PWVuZHBvaW50LCB5ID0gb3VfdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gaWQpLCBjb2wgPSAiZ3JlZW4iKSAgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBwczEsIGFlcyh4PWVuZHBvaW50LCB5ID0gb3VfdHlwZSwgc2l6ZSA9IGN1bXN1bSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD0iZ3JleSIsIGFscGhhPTAuOCkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBwczIsIGFlcyh4PWVuZHBvaW50LCB5ID0gb3VfdHlwZSwgc2l6ZSA9IGN1bXN1bSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPSJncmV5IiwgYWxwaGE9MC44KSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0cmFuc2l0aW9uX3JldmVhbChnZXN0YWdlKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9YygwLCAxLCAyKSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiR0EgMCIsIklkZW50aWZpY2F0aW9uIiwgIk90aGVyIEV2ZW50IikpICsNCiAgbGFicyh0aXRsZT0iZS1SZWcgTWF0bGFiIC0tIEV2ZW50cyBhdCBEaWZmZXJlbnQgT3JnIFVuaXQgdGhhbiBJZGVudGlmaWNhdGlvbiIsDQogICAgICAgc3VidGl0bGUgPSAnRXZlbnRzIGF0IEdlc3RhdGlvbmFsIEFnZSB7cm91bmQoZnJhbWVfYWxvbmcpfScsDQogICAgICAgeCA9ICIiKQ0KDQphbmltYXRlKHAyLA0KICAgICAgICBuZnJhbWVzID0gMjAwLA0KICAgICAgICBkdXJhdGlvbiA9IDE1LA0KICAgICAgICBlbmRfcGF1c2UgPSA1MCkNCmBgYA0KDQpJZiBhIGRvdCBtb3ZlcyBxdWlja2x5IGJldHdlZW4gaWRlbnRpZmljYXRpb24gYW5kIHN1YnNlcXVlbnQgdmlzaXQsIHRoYXQgbWVhbnMgdGhhdCB0aGUgbmV4dCB2aXNpdCBoYXBwZW5lZCBxdWlja2x5IGFmdGVyIGlkZW50aWZpY2F0aW9uIChlLmcuLCBVbml0IGlkZW50aWZpZXMgcHJlZ25hbmN5IGF0IDE2IHdlZWtzLCB2aXNpdCB0byBDSENQIGF0IDE4IHdlZWtzKS4gSW52ZXJzZWx5LCB0aGUgc2Vjb25kIHZpc2l0IG1heSBvY2N1ciBsb25nIGFmdGVyIGlkZW50aWZpY2F0aW9uIChzYW1lIGNsaWVudCB2aXNpdHMgQ0hDUCBhZ2FpbiBhdCAzMiB3ZWVrcywgdGhpcyB3b3VsZCBiZSBhIHNsb3dlciBtb3ZpbmcgZG90KS4NCg0KRXZlbnR1YWxseSB3YW50IHRvIHJlY3JlYXRlIGFzIGEgW1NhbmtleSBmbG93IGRpYWdyYW1dKGh0dHBzOi8vd3d3Lmh2aXRmZWxkdC5tZS9ibG9nL3JlY3JlYXRlLXNhbmtleS1mbG93LWNoYXJ0LykgYWNyb3NzIDQgQU5DIHZpc2l0cy4gU2VlIGJlbG93Lg0KDQoNCg0KIyMgUGFyYWxsZWwgQ29vcmRpbmF0ZXMNCg0KSWYgd2Ugd2FudCB0byBleHBsb3JlIHRoZXNlIHJlbGF0aW9uc2hpcHMgZnVydGhlciwgd2UgY2FuIHVzZSBpbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9uLg0KDQpUaGUgdHlwZSBiZWxvdyBpcyBjYWxsZWQgKnBhcmFsbGVsIGNvb3JkaW5hdGVzLiogDQoNClRoZSB2ZXJ0aWNhbCBheGVzIHJlcHJlc2VudCB2YXJpYWJsZXMuIFRoZSBob3Jpem9udGFsIGFuZCBkaWFnb25hbCBsaW5lcyBhcmUgb2JzZXJ2YXRpb25zLCB3aGVyZSBlYWNoIGlzIGFuIEVWRU5UIHRoYXQgdG9vayBhdCBhIGRpZmZlcmVudCBwbGFjZSB0aGFuIHRoZSBlbnJvbGxtZW50IG9yZyB1bml0LiBUaGUgY29sb3JzIGFyZSBiYXNlZCBvbiB0aGUgdHlwZSBvZiBlbnJvbGxtZW50IG9yZyB1bml0Lg0KDQpZb3UgY2FuIGNsaWNrIGFuZCBkcmFnIGFjcm9zcyBhbiBheGlzIHRvIHNlbGVjdCBhIHJhbmdlIGZvciBlYWNoIHZhcmlhYmxlLCBhbmQgaXQgd2lsbCBmaWx0ZXIgZG93biB0byB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgbWVldCB0aGF0IGNyaXRlcmlhLiBGb3IgZXhhbXBsZSwgb2YgdGhvc2UgY2xpZW50cyB3aG8gd2VyZSBlbnJvbGxlZCBhdCBhIFdBUkQsIGFuZCBsYXRlciB3ZW50IHRvIGEgQ0MsIHdoYXQgd2FzIHRoZSByYW5nZSBvZiBnZXN0YXRpb25hbCBhZ2VzIHdoZW4gdGhhdCBDQyB2aXNpdCB0b29rIHBsYWNlPw0KDQpgYGB7ciBwYXJjb29yZHN9DQojIyMjIyBQYXJhbGxlbCBDb29yZGluYXRlcyBHcmFwaCAjIyMjIyMNCmxpYnJhcnkocGFyY29vcmRzKQ0KI3BhcmFsbGVsIGNvb3JkaW5hdGVzIHdpdGggY29sb3IgYmFzZWQgb24gZ2VuZGVyDQoNCnBhcmNvb3Jkczo6cGFyY29vcmRzKGRhdGEgPSBwY29yZHNfZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbGlzdCgNCiAgICAgICAgICAgICAgICAgICAgICAgIyBkaXNjcmV0ZSBvciBjYXRlZ29yaWNhbCBjb2x1bW4NCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3JTY2FsZSA9ICJzY2FsZU9yZGluYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICBjb2xvckJ5ID0gImVucl9vdV90eXBlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3JTY2hlbWUgPSAic2NoZW1lQ2F0ZWdvcnkxMCIpLA0KICAgICAgICAgICAgICAgICAgICAgd2l0aEQzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIGJydXNoTW9kZSA9ICIxRC1heGVzLW11bHRpIiwNCiAgICAgICAgICAgICAgICAgICAgIGFscGhhT25CcnVzaGVkID0gMC4yLA0KICAgICAgICAgICAgICAgICAgICAgcXVldWUgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgcmF0ZSA9IDUwLA0KICAgICAgICAgICAgICAgICAgICAgcmVvcmRlcmFibGUgPSBUUlVFKQ0KDQoNCmBgYA0KDQpUbyBtYWtlIHRoZSBjb3JyZWxhdGlvbnMgZWFzaWVyIHRvIHJlYWQsIHlvdSBjYW4gZHJhZyBhbmQgcmVhcnJhbmdlIHRoZSBheGVzIG9yZGVyLg0KDQpXZSBtaWdodCBleHBhbmQgb24gdGhpcyBieSBsaW5raW5nIHRvIHRoZSBzZWxlY3RlZCByYW5nZXMgdG8gdGFibGVzLCBmb3IgZHluYW1pYyBmaWx0ZXJpbmcgb2YgZGF0YS4NCg0KDQoNCiMgRHJvcG91dHM6IElzb2xhdGluZyBDbGllbnRzIHdpdGggb25seSBvbmUgZXZlbnQNCg0KSWYgdGhlIGNsaWVudCBoYXMgb25seSBvbmUgZXZlbnQgaW4gc3lzdGVtLCBtYXliZSB0aGV5IGFyZSBkaWZmZXJlbnQgZm9yIHNvbWUgcmVhc29uIHRoYW4gdGhlIG90aGVyIGV2ZW50cy4NCg0KRm9yIGV4YW1wbGUsIHdoYXQgc3RhZ2Ugd2FzIHRoZWlyIG9ubHkgZXZlbnQ/DQoNCldoYXQga2luZCBvZiBvcmcgdW5pdD8NCg0KVGhlIGJlbG93IHRhYmxlcyBhcmUgb25seSBvbmUgZXZlbnQuIA0KDQpGaXJzdCBpcyBieSBvcmcgdW5pdCAtLSBtb3N0IG9mIHRoZXNlIGFyZSB0aGUgUHJlZ25hbmN5IElEIHN0YWdlLg0KDQpgYGB7ciBrYWJsZX0NCmxpYnJhcnkoa2FibGVFeHRyYSkNCiMjI0lzb2xhdGUgdGhvc2UgdGhhdCBvbmx5IGhhdmQgb25lIGV2ZW50DQpteV9kYXRhX2lzbzwtcmF3NCAlPiUgDQogIGxlZnRfam9pbihvdV9uYW1lcywgYnk9YygiRXZ0T3JnVW5pdCI9Im91X2lkIikpICU+JSANCiAgc2VsZWN0KHRlaT0zLCBnYT04LCAib3VfaWQiPUV2dE9yZ1VuaXQsIG91X3R5cGUsIG5hbWUsIFZpc2l0Tm8sIEVuck9yZ1VuaXQsIFN0YWdlVWlkKSAlPiUNCiAgbGVmdF9qb2luKHBzLCBieSA9IGMoIlN0YWdlVWlkIj0iaWQiKSkgJT4lIA0KICBtdXRhdGUoZ2E9cm91bmQoYXMubnVtZXJpYyhnYSkpKSAlPiUgDQogIGZpbHRlcighaXMubmEob3VfdHlwZSkgJiAhaXMubmEoZ2EpICYgDQogICAgICAgICAgIGdhIDw9IDUwICYgZ2EgPj0gMSAmDQogICAgICAgICAgc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoInJlZ25hbmMiLCAiQU5DIiwgIk5ld2Jvcm4iLCJQTkMiLCJMYWIiKSxjb2xsYXBzZSA9ICd8JykpICYNCiAgICAgICAgICAhc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoIlByZXYiLCJSaXNrIiwiTWFuYWciKSxjb2xsYXBzZSA9ICd8JykpKSAlPiUgDQogIGdyb3VwX2J5KHRlaSkgJT4lIA0KICBhZGRfdGFsbHkoKSAlPiUgDQogIGFycmFuZ2UodGVpLCBnYSkgJT4lIA0KICBtdXRhdGUoZmlyc3QgPSBkcGx5cjo6Zmlyc3Qob3VfaWQpKSAlPiUNCiAgbXV0YXRlKGxhc3Rfb3UgPSBsYWcob3VfaWQpKSAlPiUNCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKE1vdmVkX291ID0gY2FzZV93aGVuKGZpcnN0ID09IG91X2lkICYgaXMubmEobGFzdF9vdSkgIH4gIkV2ZW50IDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgPT0gb3VfaWQgJiAhaXMubmEobGFzdF9vdSkgfiAiRXZlbnQgMissIHNhbWUgb3UgYXMgRXZlbnQgMSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgIT0gb3VfaWQgfiAiRXZlbnQgMissIGRpZmZlcmVudCBvdSBhcyBFdmVudCAxIikpDQoNCiMgbXlfZGF0YV9pc28gJT4lIA0KIyAgIGdyb3VwX2J5KG91X3R5cGUsIG4pICU+JSANCiMgICBzdW1tYXJpc2UoImV2ZW50cyI9IG4oKSkgJT4lIA0KIyAgIG11dGF0ZShwZXJjZW50ID0gcm91bmQoZXZlbnRzL3N1bShldmVudHMpLCAyKSkNCg0KI215X2RhdGFfaXNvDQoNCnRlc3QyPC1teV9kYXRhX2lzbyAlPiUgDQogIGdyb3VwX2J5KHRlaSkgJT4lIA0KICBmaWx0ZXIobj09MSkgJT4lIA0KICBncm91cF9ieShwc25hbWUpICU+JSANCiAgc3VtbWFyaXplKCJzdGFnZV9jb3VudCI9bigpKQ0KDQp0ZXN0MzwtbXlfZGF0YV9pc28gJT4lIA0KICBmaWx0ZXIobj09MSkgJT4lIA0KICBncm91cF9ieShvdV90eXBlKSAlPiUgDQogIHN1bW1hcml6ZSgib3VfdHlwZV9jb3VudCI9bigpKQ0KDQoja2FibGVFeHRyYTo6a2FibGUodGVzdDIpDQprYWJsZSh0ZXN0MikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCg0KYGBgDQoNCk5leHQgaXMgYnkgb3JnIHVuaXQuIE1vc3Qgb2YgdGhlc2UgYXJlIHRoZSBGV0EgVW5pdHMuDQoNCmBgYHtyfQ0Ka2FibGUodGVzdDMpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCg0KDQpgYGANCg0KIyBDYXNjYWRlIGJ5IFZpc2l0IE51bWJlcg0KDQpUaGlzIHNlY3Rpb24gc2hvd3MgKkFMTCBWSVNJVFMqIGZvciBlYWNoIHBhdGllbnQgV2UgY2FuIHZpc3VhbGl6ZSB0aGUgdmlzaXQgbnVtYmVyICh4IGF4aXMpLCBHQSBhdCB2aXNpdCAodGltZSksIGFuZCB0eXBlIG9mIG9yZyB1bml0IGF0IGVhY2ggdmlzaXQgKHkgYXhpcykuIFRoZSBpbnNwaXJhdGlvbiBpcyB0aGlzIFtOWVRpbWVzIGluZm9ncmFwaGljXShodHRwczovL3d3dy5ueXRpbWVzLmNvbS9pbnRlcmFjdGl2ZS8yMDE4LzAzLzE5L3Vwc2hvdC9yYWNlLWNsYXNzLXdoaXRlLWFuZC1ibGFjay1tZW4uaHRtbCkNCg0KRWFjaCBkb3QgcmVwcmVzZW50cyBhIHBhdGllbnQgYXMgc2hlIG1vdmVzIHRocm91Z2ggZWFjaCBsZXZlbCBvZiB0aGUgaGVhbHRoIHN5c3RlbS4gVGhlIHBhdGllbnQncyBkb3QgInJlc3RzIiBhdCB0aGUgbG9jYXRpb24gb2YgdGhlIGxhc3QgdmlzaXQgcmVjb3JkZWQuIE5vdGUgdGhhdCB0aGUgdGhpcyBjb25zaWRlcnMgZWFjaCBpZGVudGlmaWNhdGlvbiwgQU5DIHZpc2l0LCBvciBob21lIHZpc2l0IHN0YWdlIGEgc2VwYXJhdGUgInZpc2l0Ii4gDQoNCllvdSBjYW4gc2VlIHRoYXQgdmVyeSBmZXcgcGF0aWVudHMgZ2V0IHBhc3QgdGhlIDNyZCB2aXNpdCBhdCBhbnkgbGV2ZWwuIE1vc3Qgb2YgdGhlIG1pZ3JhdGlvbiBvY2N1cnMgZnJvbSB0aGUgRldBIFVuaXQgbGV2ZWwgdXAgdGhlIHN5c3RlbSB0byBGV0MuIEFuZCBjb21wYXJlZCB0byBDQyBsZXZlbCwgbW9yZSBjbGllbnRzIHdobyBtYWRlIGl0IHRvIEZXQyBieSB0aGUgdGhpcmQgdmlzaXQgc3RhcnRlZCBmcm9tIGFub3RoZXIgb3JnIHVuaXQgKHJlZCBkb3RzKS4NCg0KYGBge3J9DQoNCmNhc2NhZGVfZGF0YSA8LW15X2RhdGEgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICByZW5hbWUoImV2ZW50X291X2lkIj1vdV9pZCwgImV2ZW50X291X3R5cGUiPW91X3R5cGUpICU+JSANCiAgc2VsZWN0KHRlaSwgZXZlbnRfb3VfdHlwZSwgImV2ZW50X2dhIj1nYSwgTW92ZWRfb3UsIFN0YWdlVWlkKSAlPiUgDQogIGxlZnRfam9pbihwcywgYnkgPSBjKCJTdGFnZVVpZCI9ImlkIikpICU+JSANCiAgZmlsdGVyKCFpcy5uYShldmVudF9vdV90eXBlKSAmICFpcy5uYShldmVudF9nYSkgJiANCiAgICAgICAgICAgZXZlbnRfZ2EgPD0gNTAgJiBldmVudF9nYSA+PSAxICYNCiAgICAgICAgICBzdHJfZGV0ZWN0KHBzbmFtZSwgcGFzdGUoYygicmVnbmFuYyIsICJBTkMiKSxjb2xsYXBzZSA9ICd8JykpICYNCiAgICAgICAgICAhc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoIlByZXYiLCJSaXNrIiwiTWFuYWciLCAiT3V0IiksY29sbGFwc2UgPSAnfCcpKSkgJT4lIA0KICBncm91cF9ieSh0ZWkpICU+JSANCiAgbXV0YXRlKCJWaXNpdE5vIj1pZl9lbHNlKHN0cl9kZXRlY3QocHNuYW1lLCAiaWRlbnQiKSwgMSwgMikpICU+JSANCiAgYXJyYW5nZShWaXNpdE5vLCBldmVudF9nYSkgJT4lIA0KICBtdXRhdGUoIlZpc2l0Tm8iPXJvd19udW1iZXIoKSkgDQoNCmNhc2NhZGVfZGF0YTwtY2FzY2FkZV9kYXRhICU+JSANCiAgbXV0YXRlKCJWaXNpdE5vIj1pZl9lbHNlKFZpc2l0Tm89PTEsIDAsIGFzLmRvdWJsZShWaXNpdE5vKSkpICU+JSANCiAgYmluZF9yb3dzKGNhc2NhZGVfZGF0YSAlPiUgIGZpbHRlcihWaXNpdE5vID09IDEpKSAlPiUgDQogIG11dGF0ZSgiTW92ZWRfb3UiPWlmX2Vsc2UoVmlzaXRObyA8PTEsICJFdmVudCAxIiwgTW92ZWRfb3UpKSAlPiUgDQogIG11dGF0ZShldmVudF9vdV90eXBlPWZhY3RvcihldmVudF9vdV90eXBlLCBsZXZlbHMgPSBjKCJVbml0IiwiV2FyZCIsIkZXQyIsIkNDIikpKSAlPiUgDQogIG11dGF0ZShncm91cGlkPSBncm91cF9pbmRpY2VzKCkpICU+JSANCiAgYXJyYW5nZSh0ZWksIFZpc2l0Tm8pICU+JSANCiAgbXV0YXRlKGdlc3RhZ2U9aWZfZWxzZShWaXNpdE5vPT0wLCAwLCBldmVudF9nYSkpICU+JSANCiAgZmlsdGVyKFZpc2l0Tm88OCkgJT4lIA0KICBtdXRhdGUoZ2VzdGFnZT1pZl9lbHNlKGdlc3RhZ2U9PWxhZyhnZXN0YWdlKSAmIGdyb3VwaWQ9PWxhZyhncm91cGlkKSAmIGdlc3RhZ2UhPTAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGdlc3RhZ2UrMiwgZ2VzdGFnZSkpDQoNCg0KDQpwMzwtZ2dwbG90KCkgKw0KICBnZW9tX2ppdHRlcihkYXRhID0gY2FzY2FkZV9kYXRhLCBhZXMoeD1WaXNpdE5vLCB5ID0gZXZlbnRfb3VfdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXBpZCwgY29sID0gTW92ZWRfb3UpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT0wLjUsIHdpZHRoID0gMC4xNSwgaGVpZ2h0PTAuMSkgICsNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHRyYW5zaXRpb25fcmV2ZWFsKGdlc3RhZ2UpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDA6NyksDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkdBIDAiLCAiRXZlbnQxIiwgIkV2ZW50MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV2ZW50MyIsIkV2ZW50NCIsIkV2ZW50NSIsICJFdmVudDYiLCJFdmVudDciKSwNCiAgICAgICAgICAgICAgICAgICBtaW5vcl9icmVha3MgPSBOVUxMKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiZ3JleSIsICJyZWQiLCAiZGFya2JsdWUiKSkgKw0KICBsYWJzKHRpdGxlPSJlLVJlZyBNYXRsYWIgLS0gRXZlbnRzIDEtNyIsDQogICAgICAgc3VidGl0bGUgPSAnRXZlbnRzIGF0IEdlc3RhdGlvbmFsIEFnZSB7cm91bmQoZnJhbWVfYWxvbmcpfScsDQogICAgICAgeCA9ICIiLA0KICAgICAgIHk9IiIpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KDQphbmltYXRlKHAzLA0KICAgICAgICBuZnJhbWVzID0gMjAwLA0KICAgICAgICBkdXJhdGlvbiA9IDE1LA0KICAgICAgICBlbmRfcGF1c2UgPSA1MCkNCg0KI2dnYW5pbWF0ZTo6YW5pbV9zYXZlKCJiZF92aXNpdDF0bzcuZ2lmIikNCg0KDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQo=